package com.glsc.ngateway.ldap.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.enums.GatewayConstant;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpSearchDto;
import com.glsc.ngateway.common.api.platform.feign.IFeignPlatformService;
import com.glsc.ngateway.common.api.platform.resp.PlatformResponse;
import com.glsc.ngateway.common.base.converter.ldap.LdapEhrEmpConverter;
import com.glsc.ngateway.common.base.domain.mysql.gateway.ldap.LdapEhrDept;
import com.glsc.ngateway.common.base.domain.mysql.gateway.ldap.LdapEhrEmp;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.User;
import com.glsc.ngateway.common.base.domain.oracle.ecology.Hrmdepartment;
import com.glsc.ngateway.common.base.domain.oracle.ecology.Hrmresource;
import com.glsc.ngateway.common.base.repo.mysql.gateway.ldap.LdapEhrDeptRepo;
import com.glsc.ngateway.common.base.repo.mysql.gateway.ldap.LdapEhrEmpRepo;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.MysqlUserRepository;
import com.glsc.ngateway.common.base.repo.oracle.ecology.HrmdepartmentRepository;
import com.glsc.ngateway.common.base.repo.oracle.ecology.HrmresourceRepository;
import com.glsc.ngateway.ldap.enums.CheckLoginStatuEnum;
import lombok.SneakyThrows;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.lang.Nullable;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextMapper;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.OrFilter;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.validation.constraints.NotBlank;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class LdapEhrEmpService {

    Logger logger = LoggerFactory.getLogger(LdapEhrEmpService.class);

    @Resource
    private LdapEhrEmpRepo ldapEhrEmpRepo;

    @Resource
    private IFeignPlatformService platformFeignService;

    @Resource
    private LdapEhrDeptRepo ldapEhrDeptRepo;

    @Resource
    private MysqlUserRepository mysqlUserRepository;

    @Resource
    private LdapTemplate ldapTemplate;

    @Resource
    private HrmresourceRepository oaEhrRepo;

    @Resource
    private HrmdepartmentRepository oaDeptRepo;

    public List<LdapEhrEmp> getUserListByDept(String department) {
        if (StringUtils.isEmpty(department)) {
            return ldapEhrEmpRepo.findAll();
        } else {
            return ldapEhrEmpRepo.findByTitle(department);
        }
    }

    public List<LdapEhrEmp> getUserListByName(String name) {
        List<LdapEhrEmp> list = ldapEhrEmpRepo.findByName(name);
        return list;
    }

    public List<LdapEhrEmpDto> findAll() {
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.findAll());
    }

    /**
     * cacheNames 设定要使用的cache的名字，必须提前定义好缓存
     * key 使用SpEL表达式来设定缓存的key，如果不设置默认方法上所有参数都会作为key的一部分
     * condition 使用SpEL表达式设定出发缓存的条件，在方法执行前生效
     * unless 使用SpEL设置不缓存的条件，这里是方法执行完生效，所以条件中可以有方法执行后的value   unless = "#result==#userId"
     * sync 用于同步的，在缓存失效（过期不存在等各种原因）的时候，如果多个线程同时访问被标注的方法则只允许一个线程通过去执行方法
     */
    @Cacheable(cacheNames = "userNameCache", key = "#userId", condition = "#userId != null")
    public String getChineseName(String userId) {
        logger.info("查询数据库获取用户名称:" + userId);
        if (StrUtil.isEmpty(userId)) {
            return "";
        }
        String name = "";
        LdapEhrEmp emp = ldapEhrEmpRepo.findByBadge(userId);
        if (emp != null) {
            name = emp.getName();
        } else {
            PlatformResponse<String> platformResponse = platformFeignService.findByUserName(name);
            String platformName = null;
            if (ObjectUtil.isNotNull(platformResponse) && platformResponse.getStatus() == 0) {
                platformName = platformResponse.getData();
            }
            if (StrUtil.isNotBlank(platformName)) {
                name = platformName;
            } else {
                name = userId;
            }
        }
        return name;
    }

    //separator 分隔符 , |
    public String getChineseNameAll(String userIdList, String separator) {
        if (StrUtil.isBlank(userIdList)) {
            return "";
        }
        return this.getChineseNameAll(StrUtil.splitTrim(userIdList, separator));
    }


    public String getChineseNameAll(List<String> userIdList) {
        List<String> names = userIdList.stream().map(this::getChineseName).collect(Collectors.toList());
        return CollectionUtil.join(names, "|");
    }

    public LdapEhrEmpDto findByBadge(String accout) {
        Optional<LdapEhrEmp> ldapEhrEmp = ldapEhrEmpRepo.findById(accout);
        //TODO:未找到则先去oa表找,未找到再去t_user表查找，作为兜底逻辑，后面需要将此接口返回类型再封装dto，将多余字段去掉
        if (ldapEhrEmp.isPresent()) {
            LdapEhrEmpDto dto = new LdapEhrEmpDto();
            BeanUtils.copyProperties(ldapEhrEmp.get(), dto);
            return dto;
        } else {
            return Optional.ofNullable(findByOa(accout)).orElseGet(() -> findByPlatform(accout));
        }
    }

    @Nullable
    private LdapEhrEmpDto findByPlatform(String accout) {
        User platformUser = mysqlUserRepository.findByUsername(accout);
        if (Objects.isNull(platformUser)) {
            return null;
        }
        Hrmdepartment oaDept = Strings.isNotBlank(platformUser.getDepartmentId()) ? oaDeptRepo.findById(Integer.valueOf(platformUser.getDepartmentId())).orElse(new Hrmdepartment()) : null;
        return LdapEhrEmpDto.builder()
                .badge(platformUser.getUsername())
                .name(platformUser.getName())
                .email(platformUser.getEmail())
                //oaDept的outKey是ehr的deptid
                .depid(Objects.nonNull(oaDept) ? Integer.valueOf(oaDept.getOutkey()) : null)
                //部门名称
                .title(platformUser.getUserDepartment())
//                    .jobId(platformUser.getJobId())
//                    .jobTitle(platformUser.getJobTitle())
//                .mobile(platformUser.getMobile())
                .status("0")
//                    .reportto(platformUser.getReportto())
                .oaUserId(Strings.isNotBlank(platformUser.getUserid()) ? Integer.valueOf(platformUser.getUserid()) : null)
                .oaDeptId(Objects.nonNull(oaDept) ? oaDept.isId() : null)
                .oaCompId(Objects.nonNull(oaDept) ? oaDept.getSubcompanyid1() : null)
//                .messagerUrl(platformUser.getMessagerurl())
                .build();
    }

    @Nullable
    public LdapEhrEmpDto findByOa(String accout) {
        Hrmresource hrmresource = oaEhrRepo.findByLoginid(accout);
        if (Objects.isNull(hrmresource)) {
            return null;
        }
        Hrmdepartment oaDept = oaDeptRepo.findById(hrmresource.getDepartmentid()).orElse(new Hrmdepartment());
        return LdapEhrEmpDto.builder()
                .badge(hrmresource.getLoginid())
                .name(hrmresource.getLastname())
                .email(hrmresource.getEmail())
                //oaDept的outKey是ehr的deptid
                .depid(Integer.valueOf(oaDept.getOutkey()))
                //部门名称
                .title(oaDept.getDepartmentname())
//                    .jobId(hrmresource.getJobId())
//                    .jobTitle(hrmresource.getJobTitle())
                .mobile(hrmresource.getMobile())
                .status(String.valueOf(hrmresource.getStatus()))
//                    .reportto(hrmresource.getReportto())
                .oaUserId(hrmresource.isId())
                .oaDeptId(hrmresource.getDepartmentid())
                .oaCompId(hrmresource.getSubcompanyid1())
                .messagerUrl(hrmresource.getMessagerurl())
                .build();
    }

    public List<LdapEhrEmp> findByBadge(List<String> userId) {
        return ldapEhrEmpRepo.findAllByBadgeIn(userId);
    }

    public LdapEhrEmpDto getLdapEhrWithDeptInfo(String userId) {
        LdapEhrEmp ldapEhr = ldapEhrEmpRepo.findById(userId).orElse(null);
        if (ldapEhr == null) {
            return null;
        }
        logger.info("gettAmEhrDepInfo查询数据:" + ldapEhr.toString());
        LdapEhrDept ldapDept = ldapEhrDeptRepo.findById(ldapEhr.getDepid()).orElse(null);
        if (ldapDept == null) {
            return null;
        }
        ldapEhr.setDeptinfo(ldapDept);
        LdapEhrDept tmpDept = ldapDept;
        while (tmpDept != null && (tmpDept.getAdminid() != null && tmpDept.getAdminid() > 0)) {
            logger.info("dept=" + tmpDept);
            LdapEhrDept parentDept = ldapEhrDeptRepo.findById(tmpDept.getAdminid()).orElse(null);

            if (parentDept != null) {
                logger.info("parent dept=" + parentDept);
                tmpDept.setParentDeptInfo(parentDept);
                tmpDept = parentDept;
            } else {
                logger.info("no admin dept");
                tmpDept = null;
            }
        }
        return LdapEhrEmpConverter.domain2Dto(ldapEhr);
    }

    public List<LdapEhrEmp> getGroupById(Integer depid) {
        return ldapEhrEmpRepo.findByDepid(depid);
    }

    public List<LdapEhrEmp> getJobGroup(Integer depid, Integer jobId) {
        return ldapEhrEmpRepo.findByDepidAndJobId(depid, jobId);
    }

    public List<LdapEhrEmpDto> findUserListByUserLeaderAccount(String userAccount) {
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.findAllByReportto(userAccount));
    }

    public List<LdapEhrEmpDto> findUserListByUserLeaderAccountList(List<String> userAccountList) {
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.findAllByReporttoIn(userAccountList));
    }

    public List<LdapEhrEmpDto> getEmpByAccounts(List<String> userAccounts) {
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.findAllByBadgeIn(userAccounts));

    }

    public List<LdapEhrEmpDto> getEmpByDepts(List<String> depts) {
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.findAllByTitleIn(depts));
    }

    public User getPlatformUser(String userName) {
        return mysqlUserRepository.findByUsername(userName);
    }

    public String getUserChineseName(@NotBlank String userName) {
        LdapEhrEmp byBadge = ldapEhrEmpRepo.findByBadge(userName);
        if (Objects.nonNull(byBadge)) {
            return byBadge.getName();
        }
        User byUsername = mysqlUserRepository.findByUsername(userName);
        if (Objects.nonNull(byUsername)) {
            return byUsername.getName();
        }
        return Strings.EMPTY;
    }

    public Boolean ldapContainsUserName(String userName) {
        //校验用户名是否存在
        return ldapTemplate.search(GatewayConstant.LDAP_OA,
                new EqualsFilter(GatewayConstant.LDAP_OA_USR, userName).encode(),
                new AbstractContextMapper<Object>() {
                    @Override
                    protected Object doMapFromContext(DirContextOperations ctx) {
                        return ctx.getNameInNamespace();
                    }
                }).size() > 0;
    }

    public CheckLoginStatuEnum checkUser(String userName) {
        //校验用户名是否存在
        List<Boolean> list = ldapTemplate.search(GatewayConstant.LDAP_OA,
                new EqualsFilter(GatewayConstant.LDAP_OA_USR, userName).encode(),
                new AbstractContextMapper<>() {
                    @SneakyThrows
                    @Override
                    protected Boolean doMapFromContext(DirContextOperations ctx) {
                        Attribute lockoutTimeAttribute = ctx.getAttributes().get("lockouttime");
                        if (lockoutTimeAttribute != null) {
                            Object lockoutTimeValue = lockoutTimeAttribute.get();
                            if (lockoutTimeValue != null) {
                                return lockoutTimeValue.toString().equals("0");
                            }
                        }
                        return false;
                    }
                });
        return CollectionUtils.isEmpty(list) ? CheckLoginStatuEnum.USER_NOT_EXIST :
                list.get(0) ? CheckLoginStatuEnum.PASSWORD_WRONG : CheckLoginStatuEnum.LOCK;
    }

    public List<Map<String, Object>> returnUserStatus(String[] usrs) {
        OrFilter orFilter = new OrFilter();
        for (String usr : usrs) {
            orFilter.or(new EqualsFilter(GatewayConstant.LDAP_OA_USR, usr));
        }
        Set<String> usrSet = ldapTemplate.search(
                        GatewayConstant.LDAP_OA,
                        orFilter.encode(),
                        new AbstractContextMapper<>() {
                            @SneakyThrows
                            @Override
                            protected Object doMapFromContext(DirContextOperations ctx) {
                                Attributes as = ctx.getAttributes();
                                return as.get(GatewayConstant.LDAP_OA_USR).get();
                            }
                        })
                .stream()
                .map(Object::toString)
                .collect(Collectors.toSet());
        return Arrays.stream(usrs).map(usr -> new HashMap<String, Object>(2) {
                    private static final long serialVersionUID = -4367026725908803037L;

                    {
                        put("userName", usr);
                        // 如果不包含表示 用户已经离职或者用户已经退休
                        put("status", usrSet.contains(usr) ? "1" : "0");
                    }
                })
                .collect(Collectors.toList());
    }

    public List<LdapEhrEmpDto> findAll(LdapEhrEmpSearchDto param) {
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.query(param));
    }

    public void save(List<LdapEhrEmpDto> data) {
        if (CollectionUtil.isNotEmpty(data)) {
            Optional.ofNullable(LdapEhrEmpConverter.dto2Domain(data)).map(ldapEhrEmpRepo::saveAll);
        }
    }


    /**
     * 根据OA用户ID列表查询对应的LdapEhrEmpDto信息。
     *
     * @param oaUserIdList 包含需要查询的OA用户ID的列表。
     * @return 返回一个LdapEhrEmpDto列表，对应于输入的OA用户ID列表中的用户信息。
     */
    public List<LdapEhrEmpDto> findByOaUserIdList(List<Integer> oaUserIdList) {
        if (CollectionUtil.isEmpty(oaUserIdList)) {
            return Collections.emptyList();
        }
        // 根据提供的OA用户ID列表查询用户信息，并将查询结果转换为Dto对象列表
        return LdapEhrEmpConverter.domain2Dto(ldapEhrEmpRepo.findAllByOaUserIdIn(oaUserIdList));
    }
}
